package uva;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.util.ArrayList;

import org.mpisws.p2p.transport.multiaddress.MultiInetSocketAddress;

import rice.Continuation;
import rice.environment.Environment;
import rice.environment.logging.Logger;
import rice.pastry.*;
import rice.p2p.commonapi.Node;
import rice.pastry.Id;
import rice.pastry.NodeIdFactory;
import rice.pastry.PastryNode;
import rice.pastry.socket.SocketPastryNodeFactory;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

public class MaliciousNodeFactory extends SocketPastryNodeFactory {
	
	protected Logger logger;

	public MaliciousNodeFactory(NodeIdFactory nf, int startPort, Environment env)
			throws IOException {
		super(nf, startPort, env);
		logger = new Logger() {
			
			@Override
			public void logException(String message, Throwable exception) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void log(String message) {
				System.out.println(message);
			}
		};
		// TODO Auto-generated constructor stub
	}
	
	public PastryNode newNode(boolean isMalicious){
		if(isMalicious){
			PastryNode node = newNode(((MaliciousRandomIdFactory)this.nidFactory).generateNodeId(true), null, true);
			node.setIsMalicious(isMalicious);
			return node;
		}
		else{
			return newNode();
		}
	}
	public PastryNode newNode(Node nodeToTarget){
		
		String generatedId = nidFactory.generateNodeId().toStringFull();
		String newIdDigest = nodeToTarget.getId().toStringFull().substring(0, 30) + generatedId.substring(30, generatedId.length());
		
		Id maliciousId = Id.build(newIdDigest);
		PastryNode malice = super.newNode(maliciousId); 
		
		return malice;
	}
	
	
	  public synchronized PastryNode newNode(final Id nodeId, InetSocketAddress pAddress, boolean isMalicious) {
		    if (pAddress == null) {
		      if (environment.getParameters().contains("external_address")) {
		        try {
		          pAddress = environment.getParameters().getInetSocketAddress("external_address");
		        } catch (IOException ioe) {
		          ioe.printStackTrace();
		        }
		      }
		    }

		    try {      
		      MultiInetSocketAddress multiAddress;
		      if (pAddress == null) {
		        multiAddress = new MultiInetSocketAddress(new InetSocketAddress(localAddress, port));
		      } else {
		        multiAddress = new MultiInetSocketAddress(pAddress, new InetSocketAddress(localAddress, port));
		      }
		      PastryNode ret = newNode(nodeId, multiAddress); // fix the method just
		      if (environment.getParameters().getBoolean(
		          "pastry_socket_increment_port_after_construction")) {
		        port++;
		      }
		      return ret;
		        
		      // below if you change
		      // this
		    } catch (BindException e) {
		      if (logger.level <= Logger.WARNING)
		        logger.logException("Warning: " , e);

		      if (environment.getParameters().getBoolean(
		          "pastry_socket_increment_port_after_construction")) {
		        port++;
		        try {
		          return newNode(nodeId, pAddress); // recursion, this will
		          // prevent from things
		          // getting too out of
		          // hand in
		          // case the node can't bind to anything, expect a
		          // StackOverflowException
		        } catch (StackOverflowError soe) {
		          if (logger.level <= Logger.SEVERE)
		            logger
		                .log("SEVERE: SocketPastryNodeFactory: Could not bind on any ports!"
		                    + soe);
		          throw soe;
		        }
		      } else {
		        // clean up Environment
		        if (this.environment.getParameters().getBoolean(
		            "pastry_factory_multipleNodes")) {
		          environment.destroy();
		        }

		        throw new RuntimeException(e);
		      }
		    } catch (IOException ioe) {

		      throw new RuntimeException(ioe);
		    }    
		  }

}
